// ==++== 
//
//   Copyright (c) Microsoft Corporation.  All rights reserved.
//
// ==--== 
//==========================================================================
//  File:       TcpSocketManager.cs 
// 
//  Summary:    Provides a base for the client and server tcp socket
//              managers. 
//
//=========================================================================

 
using System;
using System.Collections; 
using System.Globalization; 
using System.IO;
using System.Net; 
using System.Net.Sockets;
using System.Runtime.Remoting.Messaging;
using System.Text;
using System.Threading; 

 
namespace System.Runtime.Remoting.Channels.Tcp 
{
 
    // A client socket manager instance should encapsulate the socket
    //   for the purpose of reading a response
    internal abstract class TcpSocketHandler : SocketHandler
    { 
        private static byte[] s_protocolPreamble = Encoding.ASCII.GetBytes(".NET");
        private static byte[] s_protocolVersion1_0 = new byte[]{1,0}; 
 

        public TcpSocketHandler(Socket socket, Stream stream) : this(socket, null, stream) 
        {
        } // TcpSocketHandler

        public TcpSocketHandler(Socket socket, RequestQueue requestQueue, Stream stream) : 
                    base(socket, requestQueue, stream)
        { 
        } // TcpSocketHandler 

 
        private void ReadAndMatchPreamble()
        {
            // make sure that the incoming data starts with the preamble
            InternalRemotingServices.RemotingAssert( 
                s_protocolPreamble.Length == 4,
                "The preamble is supposed to be 4 bytes ('.NET'). Somebody changed it..."); 
 
            if (ReadAndMatchFourBytes(s_protocolPreamble) == false)
            { 
                throw new RemotingException(
                    CoreChannel.GetResourceString("Remoting_Tcp_ExpectingPreamble"));
            }
        } // ReadAndMatchPreamble 

        protected void WritePreambleAndVersion(Stream outputStream) 
        { 
            outputStream.Write(s_protocolPreamble, 0, s_protocolPreamble.Length);
            outputStream.Write(s_protocolVersion1_0, 0, s_protocolVersion1_0.Length); 
        } // WritePreamble


        protected void ReadVersionAndOperation(out UInt16 operation) 
        {
            // check for the preamble 
            ReadAndMatchPreamble(); 

            // Check the version number. 
            byte majorVersion = (byte)ReadByte();
            byte minorVersion = (byte)ReadByte();
            if ((majorVersion != 1) || (minorVersion != 0))
            { 
                throw new RemotingException(
                    String.Format( 
                        CultureInfo.CurrentCulture, CoreChannel.GetResourceString("Remoting_Tcp_UnknownProtocolVersion"), 
                        majorVersion.ToString(CultureInfo.CurrentCulture) + "." + minorVersion.ToString(CultureInfo.CurrentCulture)));
            } 

            // Read the operation
            operation = ReadUInt16();
 
        } // ReadVersionAndOperation
 
 
        protected void ReadContentLength(out bool chunked, out int contentLength)
        { 
            contentLength = -1;

            UInt16 header = ReadUInt16();
            if (header == TcpContentDelimiter.Chunked) 
            {
                chunked = true; 
            } 
            else
            if (header == TcpContentDelimiter.ContentLength) 
            {
                chunked = false;
                contentLength = ReadInt32();
            } 
            else
            { 
                throw new RemotingException( 
                    String.Format(
                        CultureInfo.CurrentCulture, CoreChannel.GetResourceString("Remoting_Tcp_ExpectingContentLengthHeader"), 
                        header.ToString(CultureInfo.CurrentCulture)));

            }
        } // ReadContentLength 

 
        protected void ReadToEndOfHeaders(BaseTransportHeaders headers) 
        {
            bool bError = false; 
            String statusPhrase = null;

            UInt16 headerType = ReadUInt16();
            while (headerType != TcpHeaders.EndOfHeaders) 
            {
                if (headerType == TcpHeaders.Custom) 
                { 
                    String headerName = ReadCountedString();
                    String headerValue = ReadCountedString(); 

                    headers[headerName] = headerValue;
                }
                else 
                if (headerType == TcpHeaders.RequestUri)
                { 
                    ReadAndVerifyHeaderFormat("RequestUri", TcpHeaderFormat.CountedString); 

                    // read uri (and make sure that no channel specific data is present) 
                    String uri = ReadCountedString();

                    String channelURI;
                    String objectURI; 
                    channelURI = TcpChannelHelper.ParseURL(uri, out objectURI);
                    if (channelURI == null) 
                        objectURI = uri; 

                    headers.RequestUri = objectURI; 
                }
                else
                if (headerType == TcpHeaders.StatusCode)
                { 
                    ReadAndVerifyHeaderFormat("StatusCode", TcpHeaderFormat.UInt16);
 
                    UInt16 statusCode = ReadUInt16(); 
                    // We'll throw an exception here if there was an error. If an error
                    //   occurs above the transport level, the status code will still be 
                    //   success here.
                    if (statusCode != TcpStatusCode.Success)
                        bError = true;
                } 
                else
                if (headerType == TcpHeaders.StatusPhrase) 
                { 
                    ReadAndVerifyHeaderFormat("StatusPhrase", TcpHeaderFormat.CountedString);
 
                    statusPhrase = ReadCountedString();
                }
                else
                if (headerType == TcpHeaders.ContentType) 
                {
                    ReadAndVerifyHeaderFormat("Content-Type", TcpHeaderFormat.CountedString); 
 
                    String contentType = ReadCountedString();
                    headers.ContentType = contentType; 
                }
                else
                {
                    // unknown header: Read header format and ignore rest of data 
                    byte headerFormat = (byte)ReadByte();
 
                    switch (headerFormat) 
                    {
                    case TcpHeaderFormat.Void: break; 
                    case TcpHeaderFormat.CountedString: ReadCountedString(); break;
                    case TcpHeaderFormat.Byte: ReadByte(); break;
                    case TcpHeaderFormat.UInt16: ReadUInt16(); break;
                    case TcpHeaderFormat.Int32: ReadInt32(); break; 

                    default: 
                    { 
                        // unknown format
                        throw new RemotingException( 
                            String.Format(
                                CultureInfo.CurrentCulture, CoreChannel.GetResourceString("Remoting_Tcp_UnknownHeaderType"),
                                headerType, headerFormat));
                    } 

                    } // switch (format) 
 
                }
 
                // read next header token
                headerType = ReadUInt16();
            } // loop until end of headers
 
            // if an error occurred, throw an exception
            if (bError) 
            { 
                if (statusPhrase == null)
                    statusPhrase = ""; 

                throw new RemotingException(
                    String.Format(
                        CultureInfo.CurrentCulture, CoreChannel.GetResourceString("Remoting_Tcp_GenericServerError"), 
                        statusPhrase));
            } 
        } // ReadToEndOfHeaders 

 
        protected void WriteHeaders(ITransportHeaders headers, Stream outputStream)
        {
            IEnumerator it = null;
            BaseTransportHeaders wkHeaders = headers as BaseTransportHeaders; 

            if (wkHeaders != null) 
            { 
                // write out well known headers
                //   NOTE: RequestUri is written out elsewhere. 

                if (wkHeaders.ContentType != null)
                {
                    WriteContentTypeHeader(wkHeaders.ContentType, outputStream); 
                }
 
                it = wkHeaders.GetOtherHeadersEnumerator(); 
            }
            else 
            {
                it = headers.GetEnumerator();
            }
 

            // write custom headers 
            if (it != null) 
            {
                while (it.MoveNext()) 
                {
                    DictionaryEntry header = (DictionaryEntry)it.Current;

                    String headerName = (String)header.Key; 

                    if (!StringHelper.StartsWithDoubleUnderscore(headerName)) // exclude special headers 
                    { 
                        String headerValue = header.Value.ToString();
 
                        if (wkHeaders == null)
                        {
                            if (String.Compare(headerName, "Content-Type", StringComparison.OrdinalIgnoreCase) == 0)
                            { 
                                WriteContentTypeHeader(headerValue, outputStream);
                                continue; 
                            } 
                        }
 
                        WriteCustomHeader(headerName, headerValue, outputStream);
                    }
                } // while (it.MoveNext())
            } 

            // write EndOfHeaders token 
            WriteUInt16(TcpHeaders.EndOfHeaders, outputStream); 
        } // WriteHeaders
 

        private void WriteContentTypeHeader(String value, Stream outputStream)
        {
            WriteUInt16(TcpHeaders.ContentType, outputStream); 
            WriteByte(TcpHeaderFormat.CountedString, outputStream);
            WriteCountedString(value, outputStream); 
        } // WriteContentTypeHeader 

        private void WriteCustomHeader(String name, String value, Stream outputStream) 
        {
            WriteUInt16(TcpHeaders.Custom, outputStream);
            WriteCountedString(name, outputStream);
            WriteCountedString(value, outputStream); 
        } // WriteCustomHeader
 
 

        protected String ReadCountedString() 
        {
            // strings are formatted as follows
            // [string format (1-byte)][encoded-size (int32)][string value (encoded-size length in bytes)]
 
            byte strFormat = (byte)ReadByte();
            int strDataSize = ReadInt32(); 
 
            if (strDataSize > 0)
            { 
                byte[] data = new byte[strDataSize];

                // SocketHander::Read waits until it reads all requested data
                Read(data, 0, strDataSize); 

                switch (strFormat) 
                { 
                    case TcpStringFormat.Unicode:
                        return Encoding.Unicode.GetString(data); 

                    case TcpStringFormat.UTF8:
                        return Encoding.UTF8.GetString(data);
 
                    default:
                        throw new RemotingException( 
                            String.Format( 
                                CultureInfo.CurrentCulture, CoreChannel.GetResourceString("Remoting_Tcp_UnrecognizedStringFormat"),
                                strFormat.ToString(CultureInfo.CurrentCulture))); 
                }
            }
            else
            { 
                return null;
            } 
        } // ReadCountedString 

 
        protected void WriteCountedString(String str, Stream outputStream)
        {
            // strings are formatted as follows [string length (int32)][string value (unicode)]
            int strLength = 0; 
            if (str != null)
                strLength = str.Length; 
 
            if (strLength > 0)
            { 
                byte[] strBytes = Encoding.UTF8.GetBytes(str);

                // write string format
                WriteByte(TcpStringFormat.UTF8, outputStream); 

                // write string data size 
                WriteInt32(strBytes.Length, outputStream); 

                // write string data 
                outputStream.Write(strBytes, 0, strBytes.Length);
            }
            else
            { 
                // write string format
                //   (just call it Unicode (doesn't matter since there is no data)) 
                WriteByte(TcpStringFormat.Unicode, outputStream); 

                // stream data size is 0. 
                WriteInt32(0, outputStream);
            }
        } // WriteCountedString
 

        private void ReadAndVerifyHeaderFormat(String headerName, byte expectedFormat) 
        { 
            byte headerFormat = (byte)ReadByte();
 
            if (headerFormat != expectedFormat)
            {
                throw new RemotingException(
                    String.Format( 
                        CultureInfo.CurrentCulture, CoreChannel.GetResourceString("Remoting_Tcp_IncorrectHeaderFormat"),
                        expectedFormat, headerName)); 
            } 
        } // ReadAndVerifyHeaderFormat
 
    } // TcpSocketHandler

} // namespace System.Runtime.Remoting.Channels

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// ==++== 
//
//   Copyright (c) Microsoft Corporation.  All rights reserved.
//
// ==--== 
//==========================================================================
//  File:       TcpSocketManager.cs 
// 
//  Summary:    Provides a base for the client and server tcp socket
//              managers. 
//
//=========================================================================

 
using System;
using System.Collections; 
using System.Globalization; 
using System.IO;
using System.Net; 
using System.Net.Sockets;
using System.Runtime.Remoting.Messaging;
using System.Text;
using System.Threading; 

 
namespace System.Runtime.Remoting.Channels.Tcp 
{
 
    // A client socket manager instance should encapsulate the socket
    //   for the purpose of reading a response
    internal abstract class TcpSocketHandler : SocketHandler
    { 
        private static byte[] s_protocolPreamble = Encoding.ASCII.GetBytes(".NET");
        private static byte[] s_protocolVersion1_0 = new byte[]{1,0}; 
 

        public TcpSocketHandler(Socket socket, Stream stream) : this(socket, null, stream) 
        {
        } // TcpSocketHandler

        public TcpSocketHandler(Socket socket, RequestQueue requestQueue, Stream stream) : 
                    base(socket, requestQueue, stream)
        { 
        } // TcpSocketHandler 

 
        private void ReadAndMatchPreamble()
        {
            // make sure that the incoming data starts with the preamble
            InternalRemotingServices.RemotingAssert( 
                s_protocolPreamble.Length == 4,
                "The preamble is supposed to be 4 bytes ('.NET'). Somebody changed it..."); 
 
            if (ReadAndMatchFourBytes(s_protocolPreamble) == false)
            { 
                throw new RemotingException(
                    CoreChannel.GetResourceString("Remoting_Tcp_ExpectingPreamble"));
            }
        } // ReadAndMatchPreamble 

        protected void WritePreambleAndVersion(Stream outputStream) 
        { 
            outputStream.Write(s_protocolPreamble, 0, s_protocolPreamble.Length);
            outputStream.Write(s_protocolVersion1_0, 0, s_protocolVersion1_0.Length); 
        } // WritePreamble


        protected void ReadVersionAndOperation(out UInt16 operation) 
        {
            // check for the preamble 
            ReadAndMatchPreamble(); 

            // Check the version number. 
            byte majorVersion = (byte)ReadByte();
            byte minorVersion = (byte)ReadByte();
            if ((majorVersion != 1) || (minorVersion != 0))
            { 
                throw new RemotingException(
                    String.Format( 
                        CultureInfo.CurrentCulture, CoreChannel.GetResourceString("Remoting_Tcp_UnknownProtocolVersion"), 
                        majorVersion.ToString(CultureInfo.CurrentCulture) + "." + minorVersion.ToString(CultureInfo.CurrentCulture)));
            } 

            // Read the operation
            operation = ReadUInt16();
 
        } // ReadVersionAndOperation
 
 
        protected void ReadContentLength(out bool chunked, out int contentLength)
        { 
            contentLength = -1;

            UInt16 header = ReadUInt16();
            if (header == TcpContentDelimiter.Chunked) 
            {
                chunked = true; 
            } 
            else
            if (header == TcpContentDelimiter.ContentLength) 
            {
                chunked = false;
                contentLength = ReadInt32();
            } 
            else
            { 
                throw new RemotingException( 
                    String.Format(
                        CultureInfo.CurrentCulture, CoreChannel.GetResourceString("Remoting_Tcp_ExpectingContentLengthHeader"), 
                        header.ToString(CultureInfo.CurrentCulture)));

            }
        } // ReadContentLength 

 
        protected void ReadToEndOfHeaders(BaseTransportHeaders headers) 
        {
            bool bError = false; 
            String statusPhrase = null;

            UInt16 headerType = ReadUInt16();
            while (headerType != TcpHeaders.EndOfHeaders) 
            {
                if (headerType == TcpHeaders.Custom) 
                { 
                    String headerName = ReadCountedString();
                    String headerValue = ReadCountedString(); 

                    headers[headerName] = headerValue;
                }
                else 
                if (headerType == TcpHeaders.RequestUri)
                { 
                    ReadAndVerifyHeaderFormat("RequestUri", TcpHeaderFormat.CountedString); 

                    // read uri (and make sure that no channel specific data is present) 
                    String uri = ReadCountedString();

                    String channelURI;
                    String objectURI; 
                    channelURI = TcpChannelHelper.ParseURL(uri, out objectURI);
                    if (channelURI == null) 
                        objectURI = uri; 

                    headers.RequestUri = objectURI; 
                }
                else
                if (headerType == TcpHeaders.StatusCode)
                { 
                    ReadAndVerifyHeaderFormat("StatusCode", TcpHeaderFormat.UInt16);
 
                    UInt16 statusCode = ReadUInt16(); 
                    // We'll throw an exception here if there was an error. If an error
                    //   occurs above the transport level, the status code will still be 
                    //   success here.
                    if (statusCode != TcpStatusCode.Success)
                        bError = true;
                } 
                else
                if (headerType == TcpHeaders.StatusPhrase) 
                { 
                    ReadAndVerifyHeaderFormat("StatusPhrase", TcpHeaderFormat.CountedString);
 
                    statusPhrase = ReadCountedString();
                }
                else
                if (headerType == TcpHeaders.ContentType) 
                {
                    ReadAndVerifyHeaderFormat("Content-Type", TcpHeaderFormat.CountedString); 
 
                    String contentType = ReadCountedString();
                    headers.ContentType = contentType; 
                }
                else
                {
                    // unknown header: Read header format and ignore rest of data 
                    byte headerFormat = (byte)ReadByte();
 
                    switch (headerFormat) 
                    {
                    case TcpHeaderFormat.Void: break; 
                    case TcpHeaderFormat.CountedString: ReadCountedString(); break;
                    case TcpHeaderFormat.Byte: ReadByte(); break;
                    case TcpHeaderFormat.UInt16: ReadUInt16(); break;
                    case TcpHeaderFormat.Int32: ReadInt32(); break; 

                    default: 
                    { 
                        // unknown format
                        throw new RemotingException( 
                            String.Format(
                                CultureInfo.CurrentCulture, CoreChannel.GetResourceString("Remoting_Tcp_UnknownHeaderType"),
                                headerType, headerFormat));
                    } 

                    } // switch (format) 
 
                }
 
                // read next header token
                headerType = ReadUInt16();
            } // loop until end of headers
 
            // if an error occurred, throw an exception
            if (bError) 
            { 
                if (statusPhrase == null)
                    statusPhrase = ""; 

                throw new RemotingException(
                    String.Format(
                        CultureInfo.CurrentCulture, CoreChannel.GetResourceString("Remoting_Tcp_GenericServerError"), 
                        statusPhrase));
            } 
        } // ReadToEndOfHeaders 

 
        protected void WriteHeaders(ITransportHeaders headers, Stream outputStream)
        {
            IEnumerator it = null;
            BaseTransportHeaders wkHeaders = headers as BaseTransportHeaders; 

            if (wkHeaders != null) 
            { 
                // write out well known headers
                //   NOTE: RequestUri is written out elsewhere. 

                if (wkHeaders.ContentType != null)
                {
                    WriteContentTypeHeader(wkHeaders.ContentType, outputStream); 
                }
 
                it = wkHeaders.GetOtherHeadersEnumerator(); 
            }
            else 
            {
                it = headers.GetEnumerator();
            }
 

            // write custom headers 
            if (it != null) 
            {
                while (it.MoveNext()) 
                {
                    DictionaryEntry header = (DictionaryEntry)it.Current;

                    String headerName = (String)header.Key; 

                    if (!StringHelper.StartsWithDoubleUnderscore(headerName)) // exclude special headers 
                    { 
                        String headerValue = header.Value.ToString();
 
                        if (wkHeaders == null)
                        {
                            if (String.Compare(headerName, "Content-Type", StringComparison.OrdinalIgnoreCase) == 0)
                            { 
                                WriteContentTypeHeader(headerValue, outputStream);
                                continue; 
                            } 
                        }
 
                        WriteCustomHeader(headerName, headerValue, outputStream);
                    }
                } // while (it.MoveNext())
            } 

            // write EndOfHeaders token 
            WriteUInt16(TcpHeaders.EndOfHeaders, outputStream); 
        } // WriteHeaders
 

        private void WriteContentTypeHeader(String value, Stream outputStream)
        {
            WriteUInt16(TcpHeaders.ContentType, outputStream); 
            WriteByte(TcpHeaderFormat.CountedString, outputStream);
            WriteCountedString(value, outputStream); 
        } // WriteContentTypeHeader 

        private void WriteCustomHeader(String name, String value, Stream outputStream) 
        {
            WriteUInt16(TcpHeaders.Custom, outputStream);
            WriteCountedString(name, outputStream);
            WriteCountedString(value, outputStream); 
        } // WriteCustomHeader
 
 

        protected String ReadCountedString() 
        {
            // strings are formatted as follows
            // [string format (1-byte)][encoded-size (int32)][string value (encoded-size length in bytes)]
 
            byte strFormat = (byte)ReadByte();
            int strDataSize = ReadInt32(); 
 
            if (strDataSize > 0)
            { 
                byte[] data = new byte[strDataSize];

                // SocketHander::Read waits until it reads all requested data
                Read(data, 0, strDataSize); 

                switch (strFormat) 
                { 
                    case TcpStringFormat.Unicode:
                        return Encoding.Unicode.GetString(data); 

                    case TcpStringFormat.UTF8:
                        return Encoding.UTF8.GetString(data);
 
                    default:
                        throw new RemotingException( 
                            String.Format( 
                                CultureInfo.CurrentCulture, CoreChannel.GetResourceString("Remoting_Tcp_UnrecognizedStringFormat"),
                                strFormat.ToString(CultureInfo.CurrentCulture))); 
                }
            }
            else
            { 
                return null;
            } 
        } // ReadCountedString 

 
        protected void WriteCountedString(String str, Stream outputStream)
        {
            // strings are formatted as follows [string length (int32)][string value (unicode)]
            int strLength = 0; 
            if (str != null)
                strLength = str.Length; 
 
            if (strLength > 0)
            { 
                byte[] strBytes = Encoding.UTF8.GetBytes(str);

                // write string format
                WriteByte(TcpStringFormat.UTF8, outputStream); 

                // write string data size 
                WriteInt32(strBytes.Length, outputStream); 

                // write string data 
                outputStream.Write(strBytes, 0, strBytes.Length);
            }
            else
            { 
                // write string format
                //   (just call it Unicode (doesn't matter since there is no data)) 
                WriteByte(TcpStringFormat.Unicode, outputStream); 

                // stream data size is 0. 
                WriteInt32(0, outputStream);
            }
        } // WriteCountedString
 

        private void ReadAndVerifyHeaderFormat(String headerName, byte expectedFormat) 
        { 
            byte headerFormat = (byte)ReadByte();
 
            if (headerFormat != expectedFormat)
            {
                throw new RemotingException(
                    String.Format( 
                        CultureInfo.CurrentCulture, CoreChannel.GetResourceString("Remoting_Tcp_IncorrectHeaderFormat"),
                        expectedFormat, headerName)); 
            } 
        } // ReadAndVerifyHeaderFormat
 
    } // TcpSocketHandler

} // namespace System.Runtime.Remoting.Channels

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
